#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include "table.h"

int init_database()
{
	char file_name[MAX_FILE_DIRECTORY];
	struct BLOCK_INFO model_head;
	FILE *fp = NULL;
	DIR *dp;
	if ((dp = opendir(BASE_DIRECTORY)) == NULL)
	{
		if (errno == ENOENT)
		{
			if (mkdir(BASE_DIRECTORY, 0777) == -1)
			{
				if (errno == EACCES)
					fprintf(stderr, "Permission denied!\n");
				return -1;
			}
		}
		else
			return -1;
	}

	if ((dp = opendir(DATA_DIRECTORY)) == NULL)
	{
		if (errno == ENOENT)
		{
			if (mkdir(DATA_DIRECTORY, 0777) == -1)
			{
				if (errno == EACCES)
					fprintf(stderr, "Permission denied!\n");
				return -1;
			}
		}
		else
			return -1;
	}

	strcpy(file_name, BASE_DIRECTORY);
	strcat(file_name, "model");

	if ((fp = fopen(file_name, "r")) == NULL)
	{
		if (errno == ENOENT)
		{
			if ((fp = fopen(file_name, "w")) == NULL)
			{
				if (errno == EACCES)
					fprintf(stderr, "Permission denied\n");
				return -1;
			}
			else
			{
				strcpy(model_head.szBlockMark, "model");
				model_head.iBlockNum = 0;
				fseek(fp, 0, SEEK_SET);
				fwrite(&model_head, sizeof (struct BLOCK_INFO), 1, fp);
				fclose(fp);
				if ((fp = fopen(file_name, "r")) == NULL)
					return -1;
			}
		}
		else
		{
			if (errno == EACCES)
				fprintf(stderr, "Permission denied\n");
			return -1;
		}
	}

	fseek(fp, 0, SEEK_SET);
	fread(&model_head, sizeof (struct BLOCK_INFO), 1, fp);

	if (strcmp(model_head.szBlockMark, "model"))
	{
		fprintf(stderr, "Database Head Check Error!\n");
		fclose(fp);
		return -1;
	}

	fclose(fp);
	return 1;
}

int find_field(struct TABLE stTable, char *szFieldName, int *ipType)
{
	int i, current = 0;
	for (i = 0; i < stTable.iFieldNum; ++i)
	{
		if (!strcmp(stTable.sfaFields[i].szFieldName, szFieldName))
		{
			*ipType = stTable.sfaFields[i].iType;
			return current;
		}
		if (stTable.sfaFields[i].iType == 0)
			current += sizeof (int);
		else if (stTable.sfaFields[i].iType == 1)
			current += sizeof (char) * (stTable.sfaFields[i].iLength);
	}
	if (i == stTable.iFieldNum)
		return -1;
}

int find_table(FILE *fp, char *szTableName)
{
	int i;
    int	ret = -1, pos;
	struct BLOCK_INFO model_head;
	struct TABLE 	table;
	fseek(fp, 0, SEEK_SET);
	fread(&model_head, sizeof (struct BLOCK_INFO), 1, fp);
	pos = ftell(fp);

	for (i = 0; i < model_head.iBlockNum; ++i)
	{
		fread(&table, sizeof (struct TABLE), 1, fp);
		if (!strcmp(table.szTableName, szTableName))
		{
			ret = pos;
			break;
		}
		pos += sizeof (struct TABLE);
	}
	return ret;
}

int check_table_exist(char *szTableName)
{
	DIR *dp;
	struct dirent *dirent_p;
	if ((dp = opendir(DATA_DIRECTORY)) == NULL)
		return -1;
	else
	{
		while ((dirent_p = readdir(dp)) != NULL)
			if (!strcmp(dirent_p->d_name, szTableName))
				return 1;
		return 0;
	}
	return -1;
}

int create_table(struct TABLE stNewTable)
{
	char file_name[MAX_FILE_DIRECTORY] = BASE_DIRECTORY;
	strcat(file_name, "model");
	FILE *fp = NULL, *fpx = NULL;

	if ((fp = fopen(file_name, "r+b")) == NULL)
	{
		fprintf(stderr, "Cannot open the model file!\n");
		return -1;
	}

	int check = check_table_exist(stNewTable.szTableName);
	if (check == 1)
	{
		fprintf(stderr, "Table %s already exists!\n", stNewTable.szTableName);
		fclose(fp);
		return -1;
	}
	else if (check == -1)
	{
		fprintf(stderr, "Cannot access $HOME/db/table_datas/\n");
		return -1;
	}

	strcpy(file_name, DATA_DIRECTORY);
	strcat(file_name, stNewTable.szTableName);

	if ((fpx = fopen(file_name, "w+b")) == NULL)
	{
		fprintf(stderr, "Cannot create data entry file!\n");
		return -1;
	}

	struct BLOCK_INFO model_head;
	fseek(fp, 0, SEEK_SET);
	fread(&model_head, sizeof (struct BLOCK_INFO), 1, fp);

	int position = sizeof (struct BLOCK_INFO) + sizeof (struct TABLE) * model_head.iBlockNum;
	fseek(fp, position, SEEK_SET);
	fwrite(&stNewTable, sizeof (struct TABLE), 1, fp);

	model_head.iBlockNum++;
	fseek(fp, 0, SEEK_SET);
	fwrite(&model_head, sizeof (struct BLOCK_INFO), 1, fp);
	fclose(fp);

	struct BLOCK_INFO data_head;
	strcpy(data_head.szBlockMark, "data");
	data_head.iBlockNum = 0;
	fseek(fpx, 0, SEEK_SET);
	fwrite(&data_head, sizeof (struct BLOCK_INFO), 1, fpx);
	fclose(fpx);

	printf("%s created\n", stNewTable.szTableName);
	return 1;
}

int drop_table(char *szTableName)
{
	char file_name[MAX_FILE_DIRECTORY] = BASE_DIRECTORY;
	strcat(file_name, "model");
	FILE *fp = NULL;
	
	if ((fp = fopen(file_name, "r+b")) == NULL)
		return -1;

	int check = check_table_exist(szTableName);
	if (check == 0)
	{
		fprintf(stderr, "Table %s not exists!\n", szTableName);
		return -1;
	}
	else if (check == -1)
	{
		fprintf(stderr, "Cannot access $HOME/db/table_datas/\n");
		return -1;
	}
	
	struct BLOCK_INFO model_head;
	struct TABLE table;

	fseek(fp, 0, SEEK_SET);
	fread(&model_head, sizeof (struct BLOCK_INFO), 1, fp);

	int src_pos = sizeof (struct BLOCK_INFO) + sizeof (struct TABLE) * (model_head.iBlockNum - 1);
	int dst_pos = find_table(fp, szTableName);

	fseek(fp, src_pos, SEEK_SET);
	fread(&table, sizeof (struct TABLE), 1, fp);
	fseek(fp, dst_pos, SEEK_SET);
	fwrite(&table, sizeof (struct TABLE), 1, fp);

	model_head.iBlockNum--;
	fseek(fp, 0, SEEK_SET);
	fwrite(&model_head, sizeof (struct BLOCK_INFO), 1, fp);
	fclose(fp);

	strcpy(file_name, DATA_DIRECTORY);
	strcat(file_name, szTableName);
	unlink(file_name);
	return 1;
}

int show_tables()
{
	int cnt = 0;
	DIR *dp;
	struct dirent *dirent_p;
	if ((dp = opendir(DATA_DIRECTORY)) == NULL)
	{
		fprintf(stderr, "Cannot access $HOME/db/table_datas/, please check your permission!\n");
		return -1;
	}

	puts("-------------------------------------");
	puts("Tables:");
	puts("-------------------------------------");
	
	while ((dirent_p = readdir(dp)) != NULL)
	{
		if (strcmp(".", dirent_p->d_name) && strcmp("..", dirent_p->d_name))
		{
			++cnt;
			printf("%s\n", dirent_p->d_name);
		}
	}
	
	puts("-------------------------------------");
	printf("Table num = %d\n", cnt);
	puts("-------------------------------------");
	return 1;
}

int desc_table(char *szTableName)
{
	int i;
	char file_name[MAX_FILE_DIRECTORY] = BASE_DIRECTORY;
	strcat(file_name, "model");
	FILE *fp = NULL;
	if ((fp = fopen(file_name, "r")) == NULL)
	{
		fprintf(stderr, "Cannot open the model file\n");
		return -1;
	}

	int check = check_table_exist(szTableName);
	if (check == 0)
	{
		fprintf(stderr, "Table %s not exists!\n", szTableName);
		fclose(fp);
		return -1;
	}
	else if (check == -1)
	{
		fprintf(stderr, "Cannot access $HOME/db/table_datas/\n");
		return -1;
	}
	
	int position = find_table(fp, szTableName);

	struct TABLE table;
	
	fseek(fp, position, SEEK_SET);
	fread(&table, sizeof (struct TABLE), 1, fp);
	
	puts("-------------------------------------");
	printf("Table %s, filed num = %d\n", szTableName, table.iFieldNum);
	puts("-------------------------------------");
	printf("field name\ttype\tkey\tnull\n");
	for (i = 0; i < table.iFieldNum; ++i)
	{
		printf("%-10s\t", table.sfaFields[i].szFieldName);

		if (table.sfaFields[i].iType == 0)
			printf("INT\t");
		else if (table.sfaFields[i].iType == 1)
			printf("CHAR(%d)\t", table.sfaFields[i].iLength);

		if (table.sfaFields[i].bIsKey == 1)
			printf("KEY\t");
		else
			printf("NOT KEY\t");

		if (table.sfaFields[i].bIsNull == 1)
			printf("NULL\n");
		else
			printf("NOT NULL\n");
	}
	puts("-------------------------------------");
	return 1;
}

